package tool.lexer

object Tokens {
   /** The info attached to a token (beside the position). For most
     tokens this is just the class information (eg. "it's a colon").
     For literals and identifiers, this also contains the proper
     information (eg. "this number literal represents 42"). */
   sealed trait TokenInfo {
      def tokenClass: TokenClass
   }

   /** All tokens have a corresponding token class: for instance,
     all identifiers have the IDCLASS. For "informationless"
     tokens (like COLON), the token class is the token info itself.
     This is why the trait TokenClass is mixed-in with the class in
     that case. This will be useful for the parser, the next lab.
    */
   sealed trait TokenClass {
      self =>
	 def tokenClass: self.type = self
   }


   ///    SINGLE CHARACTERS (, ), ... ///

   case object BAD extends TokenInfo with TokenClass             // represents incorrect tokens.
   case object EOF extends TokenInfo with TokenClass             // represents end of file
   case object COLON extends TokenInfo with TokenClass           // :
   case object LPAR extends TokenInfo with TokenClass
   case object RPAR extends TokenInfo with TokenClass
   case object DOT extends TokenInfo with TokenClass
   case object LBRACK extends TokenInfo with TokenClass
   case object RBRACK extends TokenInfo with TokenClass
   case object LCBRACK extends TokenInfo with TokenClass
   case object RCBRACK extends TokenInfo with TokenClass
   case object SEMICOLUMN extends TokenInfo with TokenClass
   case object ASSIGN extends TokenInfo with TokenClass

   ///  LOGICAL AND ARITHMETIC OPERATOR ///

   case object OR extends TokenInfo with TokenClass
   case object AND extends TokenInfo with TokenClass
   case object EQUALS extends TokenInfo with TokenClass
   case object LESSTHAN extends TokenInfo with TokenClass
   case object PLUS extends TokenInfo with TokenClass
   case object MINUS extends TokenInfo with TokenClass
   case object TIMES extends TokenInfo with TokenClass
   case object DIV extends TokenInfo with TokenClass
   case object NOT extends TokenInfo with TokenClass

   /// TYPES ///

   case object INT extends TokenInfo with TokenClass
   case object BOOL extends TokenInfo with TokenClass
   case object STRING extends TokenInfo with TokenClass
   
   /// KEYWORDS ///

   case object VAR extends TokenInfo with TokenClass
   case object DEF extends TokenInfo with TokenClass
   case object OBJECT extends TokenInfo with TokenClass
   case object CLASS extends TokenInfo with TokenClass
   case object MAIN extends TokenInfo with TokenClass
   case object TRUE extends TokenInfo with TokenClass
   case object FALSE extends TokenInfo with TokenClass
   case object THIS extends TokenInfo with TokenClass
   case object NEW extends TokenInfo with TokenClass
   case object INTARRAY extends TokenInfo with TokenClass // the int used in the new int[] expression
   case object ARRAYLENGTH extends TokenInfo with TokenClass // the length keyword used to get the number of elements in an array
   case object UNIT extends TokenInfo with TokenClass
   case object EXTENDS extends TokenInfo with TokenClass
    
   /// CONTROLS ///

   case object PRINTLN extends TokenInfo with TokenClass
   case object WHILE extends TokenInfo with TokenClass
   case object IF extends TokenInfo with TokenClass
   case object ELSE extends TokenInfo with TokenClass
   case object RETURN extends TokenInfo with TokenClass


   // Identifiers
   case class ID(value: String) extends TokenInfo {
       override def toString: String = "ID"+value
       def tokenClass: TokenClass = IDCLASS
   }

   // The token class for identifiers
   case object IDCLASS extends TokenClass {
       override def toString: String = "identifier"
   }


   case class INTLITERAL(v: Int) extends TokenInfo {
       def tokenClass : TokenClass = INTLITERALCLASS
       override def toString: String = "INT"+v
   }

   case object INTLITERALCLASS extends TokenClass {
       override def toString: String = "intliteral"
   }


   case class STRING(s: String) extends TokenInfo{
       def tokenClass: TokenClass = STRINGCLASS
       override def toString: String = "STRING"+s
   }

   case object STRINGCLASS extends TokenClass{
       override def toString: String = "string"
   }

   /** A Token is a positional wrapper around an info.
     You don't have to change anything in that class. */
   class Token(val info: TokenInfo) extends Positional {
      override def toString: String = info.toString
      def tokenClass: TokenClass = info.tokenClass
   }

   /** To create tokens and match on them. Nothing to change here. */
   object Token {
      def apply(info: TokenInfo): Token = new Token(info)
      def unapply(token: Token): Option[TokenInfo] = Some(token.info)
   }
}
